#coding=utf-8
'''
create on 2017-04-20

@author: sandy
'''

from twisted.internet import protocol
import struct
import logging
import time
import Const
import re
import hashlib
import base64
from WebSocketFrame import Frame
import Pack as packet
import Utils

handshake = '\
HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
Sec-WebSocket-Accept: %s\r\n\r\n\
'


class TBaseProtocol(protocol.Protocol):

    def __init__(self):
        self.__buffer = ''
        self.__contime = time.time()
        self.__lastactivetime = time.time()
        self.websocket_ready = False
        self.clientSocketType = Const.CLIENT_TCP_SOCKET
        self.requestFromBrowser = False
        

    def packetReceived(self, cmd, pkt):
        pass
    
    def getConTime(self):
        return self.__contime
    
    def getLastActiveTime(self):
        return self.__lastactivetime

    def dataReceived(self, data):
        self.__buffer += data
        logging.debug("@@@@@ dataReceived @@@@@ %s",repr(data))
        self.__lastactivetime = time.time()
        if self.clientSocketType == Const.CLIENT_TCP_SOCKET:
            try:
                self.__parseBuffer()
            except Exception:
                logging.exception(u"dataReceived()")
                self.abort()

        if self.clientSocketType == Const.CLIENT_WEB_SOCKET:
            try:
                self.webSocketData()
            except Exception:
                logging.exception(u"dataReceived()")
                self.abort()

    def __parseBuffer(self):
        blen = len(self.__buffer)
        while blen >= 4:
            plen,pcmd = packet.unpack("HH",self.__buffer[0:4])
            if blen < plen+2:
                break
            pkt = self.__buffer[4:4+plen-2];
            self.__buffer = self.__buffer[plen+2:]
            if pcmd > 0:
                self.packetReceived(pcmd, pkt)
            blen = len(self.__buffer)

    def sendPacket(self, pkt):
        plen = len(pkt)
        fmt = packet.ENDIAN_DEFAULT + "H%ds" % plen
        data = struct.pack(fmt, plen, pkt)
        #logging.debug(u"send length = %d,data = %s",len(data),repr(data))
        self.__lastactivetime = time.time()
        if self.clientSocketType == Const.CLIENT_WEB_SOCKET:
            if self.requestFromBrowser:
                data = repr(data)
            self.transport.write(Frame.buildMessage(data, mask=False))
        if self.clientSocketType == Const.CLIENT_TCP_SOCKET:
            self.transport.write(data)

    def abort(self):
        logging.debug(u"abort()")
        self.transport.abortConnection()
        
    def reset(self):
        self.__buffer = ''
        
    def sendKeepAlive(self):
        self.sendPacket(packet.pack("H",1))
        
    def isConnected(self):
        return self.connected

    def loseConnection(self):
        logging.debug(u"loseConnection")
        self.onDisconnect()
        self.transport.loseConnection()

    def abortConnection(self):
        logging.debug(u"abortConnection")
        self.onDisconnect()
        self.transport.abortConnection()

    #################################################### WebSocket ######################################
    def buildHandcheck(self):
        buf = self.__buffer
        pos = buf.find("\r\n\r\n")
        if pos == -1:
            logging.error("Incomplet Handshake")
        self.onHandshake(buf)
        cmd = buf[:pos+5]
        self.__buffer = buf[pos+4:]
        key = re.search("Sec-WebSocket-Key:\s*(\S+)\s*", cmd)
        if not key:
            logging.error("Missing Key")
        
        if cmd.find("User-Agent") != -1:
            logging.debug("Request from Browser")
            self.requestFromBrowser = True
        else:
            logging.debug("Request from Client")
            
        key = key.group(1)
        key = key+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
        key = base64.b64encode(hashlib.sha1(key).digest())
        return handshake % key

    def sendHandcheck(self):
        try:
            hc = self.buildHandcheck()
        except Exception:
            self.transport.abortConnection()
        else:
            self.transport.write(hc)
            self.websocket_ready = True

    def webSocketData(self):
        if self.websocket_ready == False:
            self.sendHandcheck()
            self.onConnect()
        else:
            frame = Frame(self.__buffer)
            self.__buffer = frame.message()
            if frame.opcode == 8:
                self.closeWebSocket()
            elif frame.opcode == 1 or frame.opcode == 2:
                logging.debug("TEXT or BINARY")
                self.__parseBuffer()
            elif frame.opcode == 9 or frame.opcode == 10:
                logging.debug("PING or PONG")
            
            logging.debug("buffer = %s",repr(self.__buffer))
    

    def closeWebSocket(self):
        logging.debug("Close WebSocket")
        self.transport.write(Frame.buildMessage(repr(''),8,mask=False))

    
    def onHandshake(self, header):
        logging.debug(u"WebSocket onHandshake")
        pass

    def onConnect(self):
        logging.debug(u"WebSocket onConnect")
        pass

    def onDisconnect(self):
        logging.debug(u"WebSocket onDisconnect")
        pass